/**
 * This is the register allocator. It is an essential part of the machine code generator.
 * Each register use is recorded in a register descriptor. 
 * @author Bennet,MLB
 */
public class VSMRegAlloc implements VSMCGConstants {
	private VSMRegDesc[] register;
	
	public VSMRegAlloc(){
		register = new VSMRegDesc[R_MAX];
		for(int i=0; i< R_MAX;i++) register[i]= new VSMRegDesc();
		//register[0].set(new Zero(), false);
	}
	
	/**
	 * spilling of all registers. Values are dumped in memory if necessary
	 */
	public void spillAll(){
		for(int i=R_RES; i<R_MAX; i++)
			register[i].spill(i);
	}
	
	/**
	 * flushAll: spill all registers and clear their descriptors
	 */
	public void flushAll(){
		for(int i=R_RES; i<R_MAX; i++){
			register[i].spill(i);
			register[i].clear();
		}
	}
	/**
	 * clear a descriptor
	 */
	
	public void clearDescriptor(int r){
		register[r].clear();
	}
	
	/**
	 * spill a register
	 */
	public void spillOne(int r){
		register[r].spill(r);
	}
	
	/**
	 * load a value in a register. If the value is in a different register, 
	 * uses LDR, if it is a constant, uses LDA indexed off R0 and if a piece
	 * of text, the address of the text is loaded with LDA. Variables are
	 * loaded from the stack with LDI. 
	 * The register descriptor is updated accordingly  
	 */
	
	public void loadRegister( int r, Operand3a op){
		// look if it is in a register
		for (int s=0; s<R_MAX; s++){
			if (register[s].getMemory() == op){
				System.out.println(R_INDENT+"LDR  R"+s+",R"+r);
				setDescriptor(r, op, register[s].getModif());
				return;
			}
		}
		// not in a register. Load appropriately with type
		if (op.isConstInteger())
			System.out.println(R_INDENT+"LDA  "+op.getName3a()+"(R0),R"+r);
		else{
			if (op.isVarInteger() || op.isPointer())
				System.out.println(R_INDENT+"LDI  "+op.getOffset()+"(R"+R_STACK+"),R"+r);
			else
				if (op.isLabel())
					System.out.println(R_INDENT+"LDA  "+op.getName3a()+",R"+r);
				else
					System.err.println("\n loadRegister: unknown case");
		}
		register[r].set(op,R_UNMODIFIED);
	}
	
	/**
	 * insert a descriptor entry for the given name
	 */
	public void setDescriptor(int r, Operand3a mem, boolean mod){
		// check if it is already in a register
		for(int re=R_GEN; re<R_MAX; re++)
			if (register[re].getMemory() == mem) register[re].clear();
		register[r].set(mem,mod); 
	}
	
	/**
	 * search for a symbol in a register, return -1 if not found
	 */
	public int search(Operand3a mem){
		int r;
		for(r = R_GEN; r<R_MAX; r++)
			if (register[r].getMemory() == mem) break;
		if (r<R_MAX) return r;
		else return -1;
	}
	
	/**
	 * get a register for one use only
	 */
	// public int getSome(int r){ }
	
	/**
	 * getErasable gets a register that will hold an operand and be
	 * overwritten by  the result.
	 */
	public int getErasable( Operand3a op){
		/*  get a register to hold the resukt of a computation a := b op c
		 * This must initially hold c and will be overwritten with a. If c
		 * is already in a register we use that, spilling it if necessary.
		 * Otherwise we use in order of preference from: 1) an empty register;
		 * 2) an unmodified register; 3) a modified register.
		 * In the last case we spill the content of the register before it
		 * is used. If c is not in the given result register, we load it.
		 * Clearly, we must use only general purpose registers. Note that 
		 * since c may be the same as b, we must update the address and 
		 * register descriptors 
		 */
		int r;
		// already in a register?
		for(r = R_GEN; r<R_MAX; r++)
			if (register[r].getMemory() == op){
				register[r].spill(r);
				return r;
			}
		// find an empty register?
		for(r = R_GEN; r<R_MAX; r++)
			if (register[r].getMemory() == null){
				loadRegister(r,op);
				return r;
			}
		// find an unmodified register?
		for(r = R_GEN; r<R_MAX; r++)
			if (!register[r].getModif()){
				register[r].clear();
				loadRegister(r,op);
				return r;
			}
		// take a modified register
		register[R_GEN].spill(R_GEN);
		register[R_GEN].clear();
		loadRegister(R_GEN,op);
		return R_GEN;
	}
	
	/**
	 * getAnother get a register to hold the second operand of a computation
	 */
	public int getAnother(Operand3a op, int cr){
		int r;
		// already in a register?
		for(r = R_GEN; r<R_MAX; r++)
			if (register[r].getMemory() == op){
				return r;
			}
		// find an empty register?
		for(r = R_GEN; r<R_MAX; r++)
			if (register[r].getMemory() == null){
				loadRegister(r,op);
				return r;
			}
		// find an unmodified register?
		for(r = R_GEN; r<R_MAX; r++)
			if (!register[r].getModif()&& (r!=cr)){
				register[r].clear();
				loadRegister(r,op);
				return r;
			}
		// take a modified register different from cr
		for(r = R_GEN; r<R_MAX; r++)
			if (r != cr){
				register[r].spill(r);
				register[r].clear();
				loadRegister(r,op);
				return r;
			}
		return -1; // this should never happend
	}
	
	/**
	 * get some register: get a register without associating it with some
	 * place in memory. We use the same policy as above to select it
	 */
	
	public int getSome(int cr){
		int r;
		// find an empty register?
		for(r = R_GEN; r<R_MAX; r++)
			if (register[r].getMemory() == null){
				return r;
			}
		// find an unmodified register?
		for(r = R_GEN; r<R_MAX; r++)
			if (!register[r].getModif()&& (r!=cr)){
				register[r].clear();
				return r;
			}
		// take a modified register (necessarely not cr as this one is newly loaded)
		register[R_GEN].spill(R_GEN);
		register[R_GEN].clear();
		return R_GEN;
	}
	
}
